/*
 * Copyright (C) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import 'dart:async';
import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List;

import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer;
import 'package:flutter/services.dart';

import 'engineIdManager.dart';

class F2NMessage {
  F2NMessage({
    this.msg,
  });

  String? msg;

  Object encode() {
    return <Object?>[
      msg,
    ];
  }

  static F2NMessage decode(Object result) {
    result as List<Object?>;
    return F2NMessage(
      msg: result[0] as String?,
    );
  }
}

class N2FMessage {
  N2FMessage({
    this.msg,
  });

  String? msg;

  Object encode() {
    return <Object?>[
      msg,
    ];
  }

  static N2FMessage decode(Object result) {
    result as List<Object?>;
    return N2FMessage(
      msg: result[0] as String?,
    );
  }
}

class _NativeChannelCodec extends StandardMessageCodec {
  const _NativeChannelCodec();
  @override
  void writeValue(WriteBuffer buffer, Object? value) {
    if (value is F2NMessage) {
      buffer.putUint8(128);
      writeValue(buffer, value.encode());
    } else {
      super.writeValue(buffer, value);
    }
  }

  @override
  Object? readValueOfType(int type, ReadBuffer buffer) {
    switch (type) {
      case 128: 
        return F2NMessage.decode(readValue(buffer)!);
      default:
        return super.readValueOfType(type, buffer);
    }
  }
}

class NativeChannel {
  /// Constructor for [NativeChannel].  The [binaryMessenger] named argument is
  /// available for dependency injection.  If it is left null, the default
  /// BinaryMessenger will be used which routes to the host platform.
  NativeChannel({BinaryMessenger? binaryMessenger})
      : _binaryMessenger = binaryMessenger;
  final BinaryMessenger? _binaryMessenger;

  static const MessageCodec<Object?> codec = _NativeChannelCodec();

  Future<void> flutterCallNative(F2NMessage arg_msg) async {
    final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
        'dev.qt_embedded_flutter.NativeChannel${EngineIdManager().getEngineId()}.flutterCallNative', codec,
        binaryMessenger: _binaryMessenger);
    final List<Object?>? replyList =
        await channel.send(<Object?>[arg_msg]) as List<Object?>?;
    if (replyList == null) {
      throw PlatformException(
        code: 'channel-error',
        message: 'Unable to establish connection on channel.',
      );
    } else if (replyList.length > 1) {
      throw PlatformException(
        code: replyList[0]! as String,
        message: replyList[1] as String?,
        details: replyList[2],
      );
    } else {
      return;
    }
  }
}

class _FlutterChannelCodec extends StandardMessageCodec {
  const _FlutterChannelCodec();
  @override
  void writeValue(WriteBuffer buffer, Object? value) {
    if (value is N2FMessage) {
      buffer.putUint8(128);
      writeValue(buffer, value.encode());
    } else {
      super.writeValue(buffer, value);
    }
  }

  @override
  Object? readValueOfType(int type, ReadBuffer buffer) {
    switch (type) {
      case 128: 
        return N2FMessage.decode(readValue(buffer)!);
      default:
        return super.readValueOfType(type, buffer);
    }
  }
}

abstract class FlutterChannel {
  static const MessageCodec<Object?> codec = _FlutterChannelCodec();

  void nativeCallFlutter(N2FMessage msg);

  static void setup(FlutterChannel? api, {BinaryMessenger? binaryMessenger}) {
    {
      final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
          'dev.qt_embedded_flutter.FlutterChannel${EngineIdManager().getEngineId()}.nativeCallFlutter', codec,
          binaryMessenger: binaryMessenger);
      if (api == null) {
        channel.setMessageHandler(null);
      } else {
        channel.setMessageHandler((Object? message) async {
          assert(message != null,
          'Argument for dev.qt_embedded_flutter.FlutterChannel.nativeCallFlutter was null.');
          final List<Object?> args = (message as List<Object?>?)!;
          final N2FMessage? arg_msg = (args[0] as N2FMessage?);
          assert(arg_msg != null,
              'Argument for dev.qt_embedded_flutter.FlutterChannel.nativeCallFlutter was null, expected non-null N2FMessage.');
          api.nativeCallFlutter(arg_msg!);
          return;
        });
      }
    }
  }
}
